// Copyright 2024 Administrator
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { MinioClientService } from "src/core/system/minio/minio.service";
import { DataSource, In, Repository } from "typeorm";
import { FavoriteForm } from "../dto/create-song.dto";
import { SongPageQuery } from "../dto/search-song.dto";
import { Song } from "../entities/song.entity";
import { ISong } from "../interface/song.interface";

@Injectable()
export class DSong implements ISong {


    constructor(
        @InjectRepository(Song) private songRepository: Repository<Song>,
        private dataSource: DataSource,
        private minioClientService: MinioClientService,
    ) { }

    /**
      * 歌曲-新增-我收藏的歌曲
      * @param songForm 
      * @returns 
      */
    async createFavorite(userId: number, { id }: FavoriteForm): Promise<void> {
        //插入favor_user_song表的数据
        await this.songRepository
            .createQueryBuilder('song')
            .relation(Song, 'favoriteUsers')
            .of(id)
            .add(userId)//多对多关系 新增与收藏者的关系
    }


    /**
    * 歌曲-删除-我收藏的歌曲
    * @param songForm 
    * @returns 
    */
    async deleteFavorite(userId: number, { id }: FavoriteForm): Promise<void> {
        await this.songRepository
            .createQueryBuilder('song')
            .relation(Song, 'favoriteUsers')
            .of(id)
            .remove(userId)//多对多关系 删除与收藏者的关系
    }

    /**
     * 歌曲-单条数据
     * @param id 
     * @param username 
     * @returns 
     */
    async getOneByIdAndUsername(id: number, username: string): Promise<Song> {
        const build = await this.songRepository
            .createQueryBuilder("song")
            .leftJoinAndSelect("song.artistList", "artist")
            .leftJoinAndSelect("song.pic", "file_info_pic")
            .leftJoinAndSelect('song.genre', "genre")
            .leftJoinAndSelect('song.video', "video")
            .leftJoinAndSelect('song.album', "album")
            .leftJoinAndSelect("song.rateFileInfos", "rate_file_info")
            .leftJoinAndSelect("rate_file_info.fileInfo", "file_info")
            .select([
                'song.id',
                'song.title',
                'song.lang',
                'song.meanVolume',
                'song.maxVolume',
                'song.lyric',
                'song.releaseDate',
                'song.pushTime',
                'song.downTime',
                'song.payModel',
                'song.createTime',
                'song.isActive',
                'song.introduce',
                'song.sort',
                'artist.id',
                'artist.name',
                'rate_file_info.id',
                'rate_file_info.rate',
                'rate_file_info.format',
                'rate_file_info.size',
                'rate_file_info.duration',
                'rate_file_info.isFull',
                'rate_file_info.expiredTime',
                'rate_file_info.startTime',
                'file_info.id',
                'file_info.creator',
                'file_info.name',
                'file_info.uid',
                'file_info.lastModified',
                'file_info.objectName',
                'file_info.bucket',
                'file_info.percentage',
                'file_info.transformProgress',
                'file_info.status',
                'file_info.size',
                'file_info.url',
                'file_info.mimeType',
                'file_info.format',
                'genre.id',
                'genre.categoryName',
                'video.id',
                'video.poster',
                'video.objectName',
                'album.id',
                'album.title',

                'file_info_pic.id',
                'file_info_pic.format',
                'file_info_pic.url',
                'file_info_pic.name',
                'file_info_pic.mimeType',
                'file_info_pic.uid',
                'file_info_pic.lastModified',
            ]).andWhere("song.id = :id", { id })


        if (username) {

            build.leftJoinAndSelect("song.favoriteUsers", "favor_user", "favor_user.username = :useranme")
            build.leftJoinAndSelect("song.paidUsers", "paid_user", "paid_user.username = :useranme")
            build.addSelect([
                'favor_user.id',
                'favor_user.username',
                'paid_user.id',
                'paid_user.username',
            ])
            build.setParameter('useranme', username)

        }

        return await build.getOne()
    }


    /**
* 歌手-歌手所有歌曲
* @param pageDto 
* @returns 
*/
    async getArtistSongList(username: string, { songName, artistIds }: SongPageQuery): Promise<[Song[], number]> {
        const build = await this.songRepository
            .createQueryBuilder("song")
            .leftJoinAndSelect("song.artistList", "artist")
            .leftJoinAndSelect("song.pic", "file_info_pic")
            .leftJoinAndSelect('song.genre', "genre")
            .leftJoinAndSelect('song.album', "album")
            .leftJoinAndSelect("song.rateFileInfos", "rate_file_info")
            .leftJoinAndSelect("rate_file_info.fileInfo", "file_info")
            .leftJoinAndSelect("song.favoriteUsers", "favor_user")
            .select([
                'song.id',
                'song.title',
                'song.lang',
                'song.meanVolume',
                'song.maxVolume',
                'song.lyric',
                'song.releaseDate',
                'song.pushTime',
                'song.downTime',
                'song.payModel',
                'song.createTime',
                'song.isActive',
                'song.sort',
                'artist.id',
                'artist.name',
                'rate_file_info.id',
                'rate_file_info.rate',
                'rate_file_info.format',
                'rate_file_info.size',
                'rate_file_info.duration',
                'rate_file_info.isFull',
                'file_info.id',
                'file_info.bucket',
                'file_info.creator',
                'file_info.name',
                'file_info.uid',
                'file_info.lastModified',
                'file_info.objectName',
                'file_info.size',
                'file_info.url',
                'file_info.mimeType',
                'genre.id',
                'genre.categoryName',
                'album.id',
                'album.title',
                'file_info_pic.id',
                'file_info_pic.percentage',
                'file_info_pic.url',
                'file_info_pic.name',
                'file_info_pic.mimeType',
                'file_info_pic.uid',
                'file_info_pic.lastModified',
            ])
            .orderBy("song.createTime", 'DESC')
            .offset(0)
            .limit(100)

        if (songName) {
            build.where('song.title like :keywords', { keywords: `%${songName}%` });
        }
       
        if (artistIds && artistIds.length > 0) {
            build.andWhere('artist.id IN (:...artistIds)', { artistIds });
        }

        const [data, total] = await build.getManyAndCount();
        return [data, total]
    }

    async myfavorite(userId: number, { pageNum, pageSize, songName }: SongPageQuery): Promise<any> {
        const build = await this.songRepository
            .createQueryBuilder("song")
            .leftJoinAndSelect("song.artistList", "artist")
            .leftJoinAndSelect("song.pic", "file_info_pic")
            .leftJoinAndSelect('song.genre', "genre")
            .leftJoinAndSelect("song.rateFileInfos", "rate_file_info")
            .leftJoinAndSelect("rate_file_info.fileInfo", "file_info")
            .leftJoinAndSelect("song.favoriteUsers", "favor_user")
            .select([
                'song.id',
                'song.title',
                'song.lang',
                'song.meanVolume',
                'song.maxVolume',
                'song.lyric',
                'song.releaseDate',
                'song.pushTime',
                'song.downTime',
                'song.payModel',
                'song.createTime',
                'song.isActive',
                'song.sort',
                'artist.id',
                'artist.name',
                'rate_file_info.id',
                'rate_file_info.rate',
                'rate_file_info.format',
                'rate_file_info.size',
                'rate_file_info.duration',
                'rate_file_info.isFull',
                'file_info.id',
                'file_info.bucket',
                'file_info.creator',
                'file_info.name',
                'file_info.uid',
                'file_info.lastModified',
                'file_info.objectName',
                'file_info.size',
                'file_info.url',
                'file_info.mimeType',
                'genre.id',
                'genre.categoryName',
                'file_info_pic.id',
                'file_info_pic.percentage',
                'file_info_pic.url',
                'file_info_pic.name',
                'file_info_pic.mimeType',
                'file_info_pic.uid',
                'file_info_pic.lastModified',
            ])
            .orderBy("song.createTime", 'DESC')
            .offset((pageNum - 1) * pageSize)
            .limit(pageSize)



        if (songName) {
            build.where('song.title like :keywords', { keywords: `%${songName}%` });
        }
        if (userId) {
            build.andWhere('favor_user.id = :userId', { userId });
        }

        const [data, total] = await build.getManyAndCount();
        return [data, total]
    }




    /**
     * 歌曲-获取分类
     * @param ids 歌曲id列表 
     */
    async getGenresByids(ids: number[]): Promise<number[]> {

        const songList = await this.songRepository.find({ where: { "id": In(ids) } })

        let cateList = songList.flatMap(song => song.genre.map(g => g.id))
        return cateList
    }

    async listBynameOrId(songName: string, artistIds: number[] = []): Promise<Song[]> {
        // return await this.songRepository.find({ where: { 'title': ILike(`%${songName}%`)} })
        const build = await this.songRepository
            .createQueryBuilder("song")
            .leftJoinAndSelect("song.artistList", "artist")
            // .leftJoinAndSelect("song.artistList", "artist", "artist.id IN (:...artistIds)")
            .setParameter('artistIds', artistIds)
            .select([
                'song.id',
                'song.title',
                'artist.id',
                'artist.name',
            ])

        if (artistIds.length > 0) {
            build.addSelect("artist.id IN (:...artistIds)")
        }
        if (songName) {
            build.where('song.title like :keywords', { keywords: `%${songName}%` });
        }

        const data = await build.getMany();

        return data;
    }


    async page(username: string, { songName, keywords, pageNum, pageSize }: SongPageQuery): Promise<[Song[], number]> {
        const build = await this.songRepository
            .createQueryBuilder("song")
            .leftJoinAndSelect("song.artistList", "artist")
            .leftJoinAndSelect("song.pic", "file_info_pic")
            .leftJoinAndSelect('song.genre', "genre")
            .leftJoinAndSelect("song.rateFileInfos", "rate_file_info")
            .leftJoinAndSelect("rate_file_info.fileInfo", "file_info")
            // .leftJoinAndSelect("song.favoriteUsers", "favor_user", "favor_user.username = :useranme")
            // .leftJoinAndSelect("song.paidUsers", "paid_user", "paid_user.username = :useranme")
            // .setParameter('useranme', username)
            .select([
                'song.id',
                'song.title',
                'song.lang',
                'song.meanVolume',
                'song.maxVolume',
                'song.lyric',
                'song.releaseDate',
                'song.pushTime',
                'song.downTime',
                'song.payModel',
                'song.createTime',
                'song.isActive',
                'song.sort',
                'artist.id',
                'artist.name',
                'rate_file_info.id',
                'rate_file_info.rate',
                'rate_file_info.format',
                'rate_file_info.size',
                'rate_file_info.duration',
                'rate_file_info.isFull',
                'file_info.id',
                'file_info.bucket',
                'file_info.creator',
                'file_info.name',
                'file_info.uid',
                'file_info.lastModified',
                'file_info.objectName',
                'file_info.size',
                'file_info.url',
                'file_info.mimeType',
                'genre.id',
                'genre.categoryName',
                // 'favor_user.id',
                // 'favor_user.username',
                // 'paid_user.id',
                // 'paid_user.username',
                'file_info_pic.id',
                'file_info_pic.percentage',
                'file_info_pic.url',
                'file_info_pic.name',
                'file_info_pic.mimeType',
                'file_info_pic.uid',
                'file_info_pic.lastModified',
            ])
            .orderBy("song.createTime", 'DESC')
            .offset((pageNum - 1) * pageSize)
            .limit(pageSize)

        if (username) {
            build.leftJoinAndSelect("song.favoriteUsers", "favor_user", "favor_user.username = :useranme")
            build.leftJoinAndSelect("song.paidUsers", "paid_user", "paid_user.username = :useranme")

            build.addSelect([
                'favor_user.id',
                'favor_user.username',
                'paid_user.id',
                'paid_user.username',
            ])
            build.setParameter('useranme', username)
        }

        if (keywords) {
            //mysql分词查询
            /**
             * 查看分词长度 SHOW VARIABLES LIKE ‘ngram_token_size’;
                修改分词长度 在MySQL的配置文件中(.ini)找到[mysqld]字样的地方，在下方添加ngram_token_size=1即可修改为支持1个字符起搜索（范围1~10）
             */
            build.where('MATCH(title,introduce) against(:keywords)', { keywords });
            // where MATCH(title,introduce) against('大飒')
        }
        
        if (songName) {
            build.where('song.title like :keywords', { keywords: `%${songName}%` });
        }

        const [data, total] = await build.getManyAndCount();
        return [data, total]
    }

    getOne(id: number): Promise<Song> {
        return this.songRepository.findOne({ where: { id } })
    }

}